﻿using Cache;
using DtoFw.Base;
using DtoFw.Query;
using Microsoft.AspNetCore.Builder;
using Serilog;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Util;

namespace Util
{
    /// <summary>
    /// WebScoket
    /// </summary>
    public class WebSocketModel
    {
        /// <summary>
        /// Socket集合keySocket对象/value当前附加的code
        /// </summary>
        private static Dictionary<string, WebSocket> dicSockets = new Dictionary<string, WebSocket>();
        /// <summary>
        /// 创建链接
        /// </summary>
        /// <param name="httpContext"></param>
        /// <param name=""></param>
        /// <returns></returns>
        private static async Task Acceptor(Microsoft.AspNetCore.Http.HttpContext httpContext, Func<Task> n)
        {
            try
            {
                if (!httpContext.WebSockets.IsWebSocketRequest)
                    return;
                var socket = await httpContext.WebSockets.AcceptWebSocketAsync();
                if (httpContext.Request.Query.Keys.Contains("userCode"))
                {
                    var userCode = httpContext.Request.Query["userCode"].ToString();
                    if (dicSockets.ContainsKey(userCode))
                    {
                        //多次登录取，记录最后一次socket
                        dicSockets[userCode] =  socket;
                    }
                    else
                    {
                        dicSockets.Add(userCode, socket);
                    }
                    var result = await RecvAsync(socket, CancellationToken.None);
                    foreach (var item in dicSockets)
                    {
                        CancellationToken cancellation = default(CancellationToken);
                        var buf = Encoding.UTF8.GetBytes(result);
                        var segment = new ArraySegment<byte>(buf);
                        if (item.Value.State == WebSocketState.Open)
                            await item.Value.SendAsync(segment, WebSocketMessageType.Text, true, cancellation);
                    }
                }
            }
            catch (Exception ex)
            {
                Exit(ex);
            }
        }
        /// <summary>
        /// 退出
        /// </summary>
        private static void Exit(Exception ex)
        {
            Log.Error(ex.Message);
            foreach (var item in dicSockets)
                item.Value.Dispose();
            dicSockets.Clear();
        }
        /// <summary>
        /// 接收客户端数据
        /// </summary>
        /// <param name="webSocket">webSocket 对象</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        private static async Task<string> RecvAsync(WebSocket webSocket, CancellationToken cancellationToken)
        {
            try
            {
                string oldRequestParam = "";
                WebSocketReceiveResult result;
                do
                {
                    var ms = new MemoryStream();
                    var buffer = new ArraySegment<byte>(new byte[1024 * 8]);
                    result = await webSocket.ReceiveAsync(buffer, cancellationToken);
                    if (result.MessageType.ToString() == "Close")
                        break;
                    ms.Write(buffer.Array, buffer.Offset, result.Count - buffer.Offset);
                    ms.Seek(0, SeekOrigin.Begin);
                    var reader = new StreamReader(ms);
                    var s = reader.ReadToEnd();
                    reader.Dispose();
                    ms.Dispose();
                    if (!string.IsNullOrEmpty(s))
                        await SendAsync(s, webSocket);
                    oldRequestParam = s;

                } while (result.EndOfMessage);
                return "";
            }
            catch (Exception ex)
            {
                Exit(ex);
                return "";
            }
        }
        /// <summary>
        /// 向客户端发送数据 
        /// </summary>
        /// <param name="msg">数据</param>
        /// <param name="webSocket">socket对象  sleep 心跳周期</param>
        /// <returns></returns>
        private static async Task SendAsync(string msg,
            WebSocket webSocket)
        {
            try
            {
                CancellationToken cancellation = default(CancellationToken);
                var buf = Encoding.UTF8.GetBytes(msg);
                var segment = new ArraySegment<byte>(buf);
                await webSocket.SendAsync(segment, WebSocketMessageType.Text, true, cancellation);
            }
            catch (Exception ex)
            {
                Exit(ex);
            }
        }
        /// <summary>
        /// 发送预警消息
        /// </summary>
        /// <param name="reqDto"></param>
        /// <returns></returns>
        public static Task<bool> SendWarning(WebScoketReqDto reqDto,List<string> userCodes)
        {
            Dictionary<int, string> keyValues = new Dictionary<int, string>() {
                { 1, "物品未绑定" },
                { 2, "未及时入库" },
                { 3, "未及时回库" },
                { 4, "异常出库" },
                { 5, "温度" },
                { 6, "湿度" },
                { 7, "烟感" },
                { 8, "水侵" },
                { 9, "空气质量" },
                { 10, "保存超期" },
                { 11, "库容爆仓" },
                { 12, "审核" }
            };
            foreach (var item in reqDto.webScoketContents)
            {
                if (!string.IsNullOrWhiteSpace(item.MolldTitle))
                    continue;
                item.MolldTitle = keyValues.FirstOrDefault(x => x.Key == item.Mold).Value;
            }
            return SendJson(Newtonsoft.Json.JsonConvert.SerializeObject(reqDto), userCodes);
        }
        /// <summary>
        /// 锁
        /// </summary>
        private static readonly object mylock = new object();
        /// <summary>
        /// 发送json字符串
        /// </summary>
        /// <param name="jsonContent">发送内容</param>
        /// <param name="userIds">发送人集合，userid</param>
        /// <returns></returns>
        public static Task<bool> SendJson(string jsonContent, List<string> userCode)
        {
            try
            {
                lock (mylock)
                {
                    if (string.IsNullOrEmpty(jsonContent))
                        return Task.FromResult(false);
                    foreach (var item in dicSockets)
                    {
                        var isSend = false;
                        if (userCode.Count < 1)
                            isSend = true;
                        if (userCode.Count > 0 && userCode.Contains(item.Key))
                            isSend = true;
                        if (item.Value.State == WebSocketState.Open && isSend)
                        {
                            CancellationToken cancellation = default(CancellationToken);
                            var buf = Encoding.UTF8.GetBytes(jsonContent);
                            var segment = new ArraySegment<byte>(buf);
                            item.Value.SendAsync(segment, WebSocketMessageType.Text, true, cancellation);
                        }
                    }
                    return Task.FromResult(true);
                }
            }
            catch (Exception ex)
            {
                Exit(ex);
                return Task.FromResult(false);
            }
        }
        /// 路由绑定处理
        /// </summary>
        /// <param name="app"></param>
        public static void Map(IApplicationBuilder app)
        {
            app.UseWebSockets(new WebSocketOptions() { 
                KeepAliveInterval = TimeSpan.FromSeconds(60),
                ReceiveBufferSize = 1024
            });
            app.Use(Acceptor);
        }
    }
}
